home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / cvs-1.8 / cvs-1 / cvs-1.8.1 / src / commit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-06  |  44.4 KB  |  1,825 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.4 kit.
  7.  * 
  8.  * Commit Files
  9.  * 
  10.  * "commit" commits the present version to the RCS repository, AFTER
  11.  * having done a test on conflicts.
  12.  *
  13.  * The call is: cvs commit [options] files...
  14.  * 
  15.  */
  16.  
  17. #include "cvs.h"
  18. #include "getline.h"
  19. #include "edit.h"
  20. #include "fileattr.h"
  21.  
  22. static Dtype check_direntproc PROTO((char *dir, char *repos, char *update_dir));
  23. static int check_fileproc PROTO((struct file_info *finfo));
  24. static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir));
  25. static int checkaddfile PROTO((char *file, char *repository, char *tag,
  26.                    char *options, RCSNode **rcsnode)); 
  27. static Dtype commit_direntproc PROTO((char *dir, char *repos, char *update_dir));
  28. static int commit_dirleaveproc PROTO((char *dir, int err, char *update_dir));
  29. static int commit_fileproc PROTO((struct file_info *finfo));
  30. static int commit_filesdoneproc PROTO((int err, char *repository, char *update_dir));
  31. static int finaladd PROTO((char *file, char *revision, char *tag,
  32.                char *options, char *update_dir,
  33.                char *repository, List *entries));
  34. static int findmaxrev PROTO((Node * p, void *closure));
  35. static int lock_RCS PROTO((char *user, char *rcs, char *rev, char *repository));
  36. static int lockrcsfile PROTO((char *file, char *repository, char *rev));
  37. static int precommit_list_proc PROTO((Node * p, void *closure));
  38. static int precommit_proc PROTO((char *repository, char *filter));
  39. static int remove_file PROTO((char *file, char *repository, char *tag,
  40.             char *message, List *entries, RCSNode *rcsnode));
  41. static void fix_rcs_modes PROTO((char *rcs, char *user));
  42. static void fixaddfile PROTO((char *file, char *repository));
  43. static void fixbranch PROTO((char *file, char *repository, char *branch));
  44. static void unlockrcs PROTO((char *file, char *repository));
  45. static void ci_delproc PROTO((Node *p));
  46. static void masterlist_delproc PROTO((Node *p));
  47. static void locate_rcs PROTO((char *file, char *repository, char *rcs));
  48.  
  49. struct commit_info
  50. {
  51.     Ctype status;            /* as returned from Classify_File() */
  52.     char *rev;                /* a numeric rev, if we know it */
  53.     char *tag;                /* any sticky tag, or -r option */
  54.     char *options;            /* Any sticky -k option */
  55. };
  56. struct master_lists
  57. {
  58.     List *ulist;            /* list for Update_Logfile */
  59.     List *cilist;            /* list with commit_info structs */
  60. };
  61.  
  62. static int force_ci = 0;
  63. static int got_message;
  64. static int run_module_prog = 1;
  65. static int aflag;
  66. static char *tag;
  67. static char *write_dirtag;
  68. static char *logfile;
  69. static List *mulist;
  70. static char *message;
  71. static time_t last_register_time;
  72.  
  73.  
  74. static const char *const commit_usage[] =
  75. {
  76.     "Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n",
  77.     "\t-n\tDo not run the module program (if any).\n",
  78.     "\t-R\tProcess directories recursively.\n",
  79.     "\t-l\tLocal directory only (not recursive).\n",
  80.     "\t-f\tForce the file to be committed; disables recursion.\n",
  81.     "\t-F file\tRead the log message from file.\n",
  82.     "\t-m msg\tLog message.\n",
  83.     "\t-r rev\tCommit to this branch or trunk revision.\n",
  84.     NULL
  85. };
  86.  
  87. #ifdef CLIENT_SUPPORT
  88. struct find_data {
  89.     List *ulist;
  90.     int argc;
  91.     char **argv;
  92. };
  93.  
  94. /* Pass as a static until we get around to fixing start_recursion to
  95.    pass along a void * where we can stash it.  */
  96. struct find_data *find_data_static;
  97.  
  98. static int find_fileproc PROTO ((struct file_info *finfo));
  99.  
  100. /* Machinery to find out what is modified, added, and removed.  It is
  101.    possible this should be broken out into a new client_classify function;
  102.    merging it with classify_file is almost sure to be a mess, though,
  103.    because classify_file has all kinds of repository processing.  */
  104. static int
  105. find_fileproc (finfo)
  106.     struct file_info *finfo;
  107. {
  108.     Vers_TS *vers;
  109.     enum classify_type status;
  110.     Node *node;
  111.     struct find_data *args = find_data_static;
  112.  
  113.     vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL,
  114.                (char *)NULL,
  115.                finfo->file, 0, 0, finfo->entries, (RCSNode *)NULL);
  116.     if (vers->ts_user == NULL
  117.     && vers->vn_user != NULL
  118.     && vers->vn_user[0] == '-')
  119.     status = T_REMOVED;
  120.     else if (vers->vn_user == NULL)
  121.     {
  122.     if (vers->ts_user == NULL)
  123.         error (0, 0, "nothing known about `%s'", finfo->fullname);
  124.     else
  125.         error (0, 0, "use `cvs add' to create an entry for %s",
  126.            finfo->fullname);
  127.     return 1;
  128.     }
  129.     else if (vers->ts_user != NULL
  130.          && vers->vn_user != NULL
  131.          && vers->vn_user[0] == '0')
  132.     status = T_ADDED;
  133.     else if (vers->ts_user != NULL
  134.          && vers->ts_rcs != NULL
  135.          && strcmp (vers->ts_user, vers->ts_rcs) != 0)
  136.     status = T_MODIFIED;
  137.     else
  138.     {
  139.     /* This covers unmodified files, as well as a variety of other
  140.        cases.  FIXME: we probably should be printing a message and
  141.        returning 1 for many of those cases (but I'm not sure
  142.        exactly which ones).  */
  143.     return 0;
  144.     }
  145.  
  146.     node = getnode ();
  147.     node->key = xstrdup (finfo->fullname);
  148.  
  149.     node->type = UPDATE;
  150.     node->delproc = update_delproc;
  151.     node->data = (char *) status;
  152.     (void)addnode (args->ulist, node);
  153.  
  154.     ++args->argc;
  155.  
  156.     return 0;
  157. }
  158.  
  159. static int copy_ulist PROTO ((Node *, void *));
  160.  
  161. static int
  162. copy_ulist (node, data)
  163.     Node *node;
  164.     void *data;
  165. {
  166.     struct find_data *args = (struct find_data *)data;
  167.     args->argv[args->argc++] = node->key;
  168.     return 0;
  169. }
  170. #endif /* CLIENT_SUPPORT */
  171.  
  172. int
  173. commit (argc, argv)
  174.     int argc;
  175.     char **argv;
  176. {
  177.     int c;
  178.     int err = 0;
  179.     int local = 0;
  180.  
  181.     if (argc == -1)
  182.     usage (commit_usage);
  183.  
  184. #ifdef CVS_BADROOT
  185.     /*
  186.      * For log purposes, do not allow "root" to commit files.  If you look
  187.      * like root, but are really logged in as a non-root user, it's OK.
  188.      */
  189.     if (geteuid () == (uid_t) 0)
  190.     {
  191.     struct passwd *pw;
  192.  
  193.     if ((pw = (struct passwd *) getpwnam (getcaller ())) == NULL)
  194.         error (1, 0, "you are unknown to this system");
  195.     if (pw->pw_uid == (uid_t) 0)
  196.         error (1, 0, "cannot commit files as 'root'");
  197.     }
  198. #endif /* CVS_BADROOT */
  199.  
  200.     optind = 1;
  201.     while ((c = getopt (argc, argv, "nlRm:fF:r:")) != -1)
  202.     {
  203.     switch (c)
  204.     {
  205.         case 'n':
  206.         run_module_prog = 0;
  207.         break;
  208.         case 'm':
  209. #ifdef FORCE_USE_EDITOR
  210.         use_editor = TRUE;
  211. #else
  212.         use_editor = FALSE;
  213. #endif
  214.         if (message)
  215.         {
  216.             free (message);
  217.             message = NULL;
  218.         }
  219.  
  220.         message = xstrdup(optarg);
  221.         break;
  222.         case 'r':
  223.         if (tag)
  224.             free (tag);
  225.         tag = xstrdup (optarg);
  226.         break;
  227.         case 'l':
  228.         local = 1;
  229.         break;
  230.         case 'R':
  231.         local = 0;
  232.         break;
  233.         case 'f':
  234.         force_ci = 1;
  235.         local = 1;        /* also disable recursion */
  236.         break;
  237.         case 'F':
  238. #ifdef FORCE_USE_EDITOR
  239.         use_editor = TRUE;
  240. #else
  241.         use_editor = FALSE;
  242. #endif
  243.         logfile = optarg;
  244.         break;
  245.         case '?':
  246.         default:
  247.         usage (commit_usage);
  248.         break;
  249.     }
  250.     }
  251.     argc -= optind;
  252.     argv += optind;
  253.  
  254.     /* numeric specified revision means we ignore sticky tags... */
  255.     if (tag && isdigit (*tag))
  256.     {
  257.     aflag = 1;
  258.     /* strip trailing dots */
  259.     while (tag[strlen (tag) - 1] == '.')
  260.         tag[strlen (tag) - 1] = '\0';
  261.     }
  262.  
  263.     /* some checks related to the "-F logfile" option */
  264.     if (logfile)
  265.     {
  266.     int n, logfd;
  267.     struct stat statbuf;
  268.  
  269.     if (message)
  270.         error (1, 0, "cannot specify both a message and a log file");
  271.  
  272.     /* FIXME: Why is this binary?  Needs more investigation.  */
  273.     if ((logfd = open (logfile, O_RDONLY | OPEN_BINARY)) < 0)
  274.         error (1, errno, "cannot open log file %s", logfile);
  275.  
  276.     if (fstat(logfd, &statbuf) < 0)
  277.         error (1, errno, "cannot find size of log file %s", logfile);
  278.  
  279.     message = xmalloc (statbuf.st_size + 1);
  280.  
  281.     /* FIXME: Should keep reading until EOF, rather than assuming the
  282.        first read gets the whole thing.  */
  283.     if ((n = read (logfd, message, statbuf.st_size + 1)) < 0)
  284.         error (1, errno, "cannot read log message from %s", logfile);
  285.  
  286.     (void) close (logfd);
  287.     message[n] = '\0';
  288.     }
  289.  
  290. #ifdef CLIENT_SUPPORT
  291.     if (client_active) 
  292.     {
  293.     struct find_data find_args;
  294.  
  295.     ign_setup ();
  296.  
  297.     /* Note that we don't do ignore file processing here, and we
  298.        don't call ignore_files.  This means that we won't print "?
  299.        foo" for stray files.  Sounds OK, the doc only promises
  300.        that update does that.  */
  301.     find_args.ulist = getlist ();
  302.     find_args.argc = 0;
  303.     find_data_static = &find_args;
  304.     err = start_recursion (find_fileproc, (FILESDONEPROC) NULL,
  305.                 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
  306.                 argc, argv, local, W_LOCAL, 0, 0,
  307.                 (char *)NULL, 0, 0);
  308.     if (err)
  309.         error (1, 0, "correct above errors first!");
  310.  
  311.     if (find_args.argc == 0)
  312.         return 0;
  313.  
  314.     /* Now we keep track of which files we actually are going to
  315.        operate on, and only work with those files in the future.
  316.        This saves time--we don't want to search the file system
  317.        of the working directory twice.  */
  318.     find_args.argv = (char **) xmalloc (find_args.argc * sizeof (char **));
  319.     find_args.argc = 0;
  320.     walklist (find_args.ulist, copy_ulist, &find_args);
  321.  
  322.     /*
  323.      * Do this before calling do_editor; don't ask for a log
  324.      * message if we can't talk to the server.  But do it after we
  325.      * have made the checks that we can locally (to more quickly
  326.      * catch syntax errors, the case where no files are modified,
  327.      * added or removed, etc.).  */
  328.     start_server ();
  329.  
  330.     /*
  331.      * We do this once, not once for each directory as in normal CVS.
  332.      * The protocol is designed this way.  This is a feature.
  333.      */
  334.     if (use_editor)
  335.         do_editor (".", &message, (char *)NULL, find_args.ulist);
  336.  
  337.     /* We always send some sort of message, even if empty.  */
  338.     option_with_arg ("-m", message);
  339.  
  340.     if (local)
  341.         send_arg("-l");
  342.     if (force_ci)
  343.         send_arg("-f");
  344.     if (!run_module_prog)
  345.         send_arg("-n");
  346.     option_with_arg ("-r", tag);
  347.  
  348.     /* Sending only the names of the files which were modified, added,
  349.        or removed means that the server will only do an up-to-date
  350.        check on those files.  This is different from local CVS and
  351.        previous versions of client/server CVS, but it probably is a Good
  352.        Thing, or at least Not Such A Bad Thing.  */
  353.     send_file_names (find_args.argc, find_args.argv, 0);
  354.     send_files (find_args.argc, find_args.argv, local, 0);
  355.  
  356.     send_to_server ("ci\012", 0);
  357.     return get_responses_and_close ();
  358.     }
  359. #endif
  360.  
  361.     if (tag != NULL)
  362.     tag_check_valid (tag, argc, argv, local, aflag, "");
  363.  
  364.     /* XXX - this is not the perfect check for this */
  365.     if (argc <= 0)
  366.     write_dirtag = tag;
  367.  
  368.     wrap_setup ();
  369.  
  370.     lock_tree_for_write (argc, argv, local, aflag);
  371.  
  372.     /*
  373.      * Set up the master update list
  374.      */
  375.     mulist = getlist ();
  376.  
  377.     /*
  378.      * Run the recursion processor to verify the files are all up-to-date
  379.      */
  380.     err = start_recursion (check_fileproc, check_filesdoneproc,
  381.                check_direntproc, (DIRLEAVEPROC) NULL, argc,
  382.                argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1,
  383.                0);
  384.     if (err)
  385.     {
  386.     lock_tree_cleanup ();
  387.     error (1, 0, "correct above errors first!");
  388.     }
  389.  
  390.     /*
  391.      * Run the recursion processor to commit the files
  392.      */
  393.     if (noexec == 0)
  394.     err = start_recursion (commit_fileproc, commit_filesdoneproc,
  395.                    commit_direntproc, commit_dirleaveproc,
  396.                    argc, argv, local, W_LOCAL, aflag, 0,
  397.                    (char *) NULL, 1, 0);
  398.  
  399.     /*
  400.      * Unlock all the dirs and clean up
  401.      */
  402.     lock_tree_cleanup ();
  403.     dellist (&mulist);
  404.  
  405.     if (last_register_time)
  406.     {
  407.     time_t now;
  408.  
  409.     (void) time (&now);
  410.     if (now == last_register_time) 
  411.     {
  412.         sleep (1);            /* to avoid time-stamp races */
  413.     }
  414.     }
  415.  
  416.     return (err);
  417. }
  418.  
  419. /*
  420.  * Check to see if a file is ok to commit and make sure all files are
  421.  * up-to-date
  422.  */
  423. /* ARGSUSED */
  424. static int
  425. check_fileproc (finfo)
  426.     struct file_info *finfo;
  427. {
  428.     Ctype status;
  429.     char *xdir;
  430.     Node *p;
  431.     List *ulist, *cilist;
  432.     Vers_TS *vers;
  433.     struct commit_info *ci;
  434.     int save_noexec, save_quiet, save_really_quiet;
  435.  
  436.     save_noexec = noexec;
  437.     save_quiet = quiet;
  438.     save_really_quiet = really_quiet;
  439.     noexec = quiet = really_quiet = 1;
  440.  
  441.     /* handle specified numeric revision specially */
  442.     if (tag && isdigit (*tag))
  443.     {
  444.     /* If the tag is for the trunk, make sure we're at the head */
  445.     if (numdots (tag) < 2)
  446.     {
  447.         status = Classify_File (finfo->file, (char *) NULL, (char *) NULL,
  448.                     (char *) NULL, 1, aflag, finfo->repository,
  449.                     finfo->entries, finfo->rcs, &vers, finfo->update_dir, 0);
  450.         if (status == T_UPTODATE || status == T_MODIFIED ||
  451.         status == T_ADDED)
  452.         {
  453.         Ctype xstatus;
  454.  
  455.         freevers_ts (&vers);
  456.         xstatus = Classify_File (finfo->file, tag, (char *) NULL,
  457.                      (char *) NULL, 1, aflag, finfo->repository,
  458.                      finfo->entries, finfo->rcs, &vers, finfo->update_dir,
  459.                      0);
  460.         if (xstatus == T_REMOVE_ENTRY)
  461.             status = T_MODIFIED;
  462.         else if (status == T_MODIFIED && xstatus == T_CONFLICT)
  463.             status = T_MODIFIED;
  464.         else
  465.             status = xstatus;
  466.         }
  467.     }
  468.     else
  469.     {
  470.         char *xtag, *cp;
  471.  
  472.         /*
  473.          * The revision is off the main trunk; make sure we're
  474.          * up-to-date with the head of the specified branch.
  475.          */
  476.         xtag = xstrdup (tag);
  477.         if ((numdots (xtag) & 1) != 0)
  478.         {
  479.         cp = strrchr (xtag, '.');
  480.         *cp = '\0';
  481.         }
  482.         status = Classify_File (finfo->file, xtag, (char *) NULL,
  483.                     (char *) NULL, 1, aflag, finfo->repository,
  484.                     finfo->entries, finfo->rcs, &vers, finfo->update_dir, 0);
  485.         if ((status == T_REMOVE_ENTRY || status == T_CONFLICT)
  486.         && (cp = strrchr (xtag, '.')) != NULL)
  487.         {
  488.         /* pluck one more dot off the revision */
  489.         *cp = '\0';
  490.         freevers_ts (&vers);
  491.         status = Classify_File (finfo->file, xtag, (char *) NULL,
  492.                     (char *) NULL, 1, aflag, finfo->repository,
  493.                     finfo->entries, finfo->rcs, &vers, finfo->update_dir,
  494.                     0);
  495.         if (status == T_UPTODATE || status == T_REMOVE_ENTRY)
  496.             status = T_MODIFIED;
  497.         }
  498.         /* now, muck with vers to make the tag correct */
  499.         free (vers->tag);
  500.         vers->tag = xstrdup (tag);
  501.         free (xtag);
  502.     }
  503.     }
  504.     else
  505.     status = Classify_File (finfo->file, tag, (char *) NULL, (char *) NULL,
  506.                 1, 0, finfo->repository, finfo->entries, finfo->rcs, &vers,
  507.                 finfo->update_dir, 0);
  508.     noexec = save_noexec;
  509.     quiet = save_quiet;
  510.     really_quiet = save_really_quiet;
  511.  
  512.     /*
  513.      * If the force-commit option is enabled, and the file in question
  514.      * appears to be up-to-date, just make it look modified so that
  515.      * it will be committed.
  516.      */
  517.     if (force_ci && status == T_UPTODATE)
  518.     status = T_MODIFIED;
  519.  
  520.     switch (status)
  521.     {
  522.     case T_CHECKOUT:
  523. #ifdef SERVER_SUPPORT
  524.     case T_PATCH:
  525. #endif
  526.     case T_NEEDS_MERGE:
  527.     case T_CONFLICT:
  528.     case T_REMOVE_ENTRY:
  529.         error (0, 0, "Up-to-date check failed for `%s'", finfo->fullname);
  530.         freevers_ts (&vers);
  531.         return (1);
  532.     case T_MODIFIED:
  533.     case T_ADDED:
  534.     case T_REMOVED:
  535.         /*
  536.          * some quick sanity checks; if no numeric -r option specified:
  537.          *    - can't have a sticky date
  538.          *    - can't have a sticky tag that is not a branch
  539.          * Also,
  540.          *    - if status is T_REMOVED, can't have a numeric tag
  541.          *    - if status is T_ADDED, rcs file must not exist
  542.          *    - if status is T_ADDED, can't have a non-trunk numeric rev
  543.          *    - if status is T_MODIFIED and a Conflict marker exists, don't
  544.          *    allow the commit if timestamp is identical or if we find
  545.          *    an RCS_MERGE_PAT in the file.
  546.          */
  547.         if (!tag || !isdigit (*tag))
  548.         {
  549.         if (vers->date)
  550.         {
  551.             error (0, 0,
  552.                "cannot commit with sticky date for file `%s'",
  553.                finfo->fullname);
  554.             freevers_ts (&vers);
  555.             return (1);
  556.         }
  557.         if (status == T_MODIFIED && vers->tag &&
  558.             !RCS_isbranch (finfo->rcs, vers->tag))
  559.         {
  560.             error (0, 0,
  561.                "sticky tag `%s' for file `%s' is not a branch",
  562.                vers->tag, finfo->fullname);
  563.             freevers_ts (&vers);
  564.             return (1);
  565.         }
  566.         }
  567.         if (status == T_MODIFIED && !force_ci && vers->ts_conflict)
  568.         {
  569.         char *filestamp;
  570.         int retcode;
  571.  
  572.         /*
  573.          * We found a "conflict" marker.
  574.          *
  575.          * If the timestamp on the file is the same as the
  576.          * timestamp stored in the Entries file, we block the commit.
  577.          */
  578. #ifdef SERVER_SUPPORT
  579.         if (server_active)
  580.             retcode = vers->ts_conflict[0] != '=';
  581.         else {
  582.             filestamp = time_stamp (finfo->file);
  583.             retcode = strcmp (vers->ts_conflict, filestamp);
  584.             free (filestamp);
  585.         }
  586. #else
  587.         filestamp = time_stamp (finfo->file);
  588.         retcode = strcmp (vers->ts_conflict, filestamp);
  589.         free (filestamp);
  590. #endif
  591.         if (retcode == 0)
  592.         {
  593.             error (0, 0,
  594.               "file `%s' had a conflict and has not been modified",
  595.                finfo->fullname);
  596.             freevers_ts (&vers);
  597.             return (1);
  598.         }
  599.  
  600.         /*
  601.          * If the timestamps differ, look for Conflict indicators
  602.          * in the file to see if we should block the commit anyway
  603.          */
  604.         run_setup ("%s", GREP);
  605.         run_arg (RCS_MERGE_PAT);
  606.         run_arg (finfo->file);
  607.         retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_REALLY);
  608.             
  609.         if (retcode == -1)
  610.         {
  611.             error (1, errno,
  612.                "fork failed while examining conflict in `%s'",
  613.                finfo->fullname);
  614.         }
  615.         else if (retcode == 0)
  616.         {
  617.             error (0, 0,
  618.                "file `%s' still contains conflict indicators",
  619.                finfo->fullname);
  620.             freevers_ts (&vers);
  621.             return (1);
  622.         }
  623.         }
  624.  
  625.         if (status == T_REMOVED && vers->tag && isdigit (*vers->tag))
  626.         {
  627.         error (0, 0,
  628.     "cannot remove file `%s' which has a numeric sticky tag of `%s'",
  629.                finfo->fullname, vers->tag);
  630.         freevers_ts (&vers);
  631.         return (1);
  632.         }
  633.         if (status == T_ADDED)
  634.         {
  635.         char rcs[PATH_MAX];
  636.  
  637.         /* Don't look in the attic; if it exists there we will
  638.            move it back out in checkaddfile.  */
  639.         sprintf(rcs, "%s/%s%s", finfo->repository, finfo->file, RCSEXT);
  640.         if (isreadable (rcs))
  641.         {
  642.             error (0, 0,
  643.         "cannot add file `%s' when RCS file `%s' already exists",
  644.                finfo->fullname, rcs);
  645.             freevers_ts (&vers);
  646.             return (1);
  647.         }
  648.         if (vers->tag && isdigit (*vers->tag) &&
  649.             numdots (vers->tag) > 1)
  650.         {
  651.             error (0, 0,
  652.         "cannot add file `%s' with revision `%s'; must be on trunk",
  653.                    finfo->fullname, vers->tag);
  654.             freevers_ts (&vers);
  655.             return (1);
  656.         }
  657.         }
  658.  
  659.         /* done with consistency checks; now, to get on with the commit */
  660.         if (finfo->update_dir[0] == '\0')
  661.         xdir = ".";
  662.         else
  663.         xdir = finfo->update_dir;
  664.         if ((p = findnode (mulist, xdir)) != NULL)
  665.         {
  666.         ulist = ((struct master_lists *) p->data)->ulist;
  667.         cilist = ((struct master_lists *) p->data)->cilist;
  668.         }
  669.         else
  670.         {
  671.         struct master_lists *ml;
  672.  
  673.         ulist = getlist ();
  674.         cilist = getlist ();
  675.         p = getnode ();
  676.         p->key = xstrdup (xdir);
  677.         p->type = UPDATE;
  678.         ml = (struct master_lists *)
  679.             xmalloc (sizeof (struct master_lists));
  680.         ml->ulist = ulist;
  681.         ml->cilist = cilist;
  682.         p->data = (char *) ml;
  683.         p->delproc = masterlist_delproc;
  684.         (void) addnode (mulist, p);
  685.         }
  686.  
  687.         /* first do ulist, then cilist */
  688.         p = getnode ();
  689.         p->key = xstrdup (finfo->file);
  690.         p->type = UPDATE;
  691.         p->delproc = update_delproc;
  692.         p->data = (char *) status;
  693.         (void) addnode (ulist, p);
  694.  
  695.         p = getnode ();
  696.         p->key = xstrdup (finfo->file);
  697.         p->type = UPDATE;
  698.         p->delproc = ci_delproc;
  699.         ci = (struct commit_info *) xmalloc (sizeof (struct commit_info));
  700.         ci->status = status;
  701.         if (vers->tag)
  702.         if (isdigit (*vers->tag))
  703.             ci->rev = xstrdup (vers->tag);
  704.         else
  705.             ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
  706.         else
  707.         ci->rev = (char *) NULL;
  708.         ci->tag = xstrdup (vers->tag);
  709.         ci->options = xstrdup(vers->options);
  710.         p->data = (char *) ci;
  711.         (void) addnode (cilist, p);
  712.         break;
  713.     case T_UNKNOWN:
  714.         error (0, 0, "nothing known about `%s'", finfo->fullname);
  715.         freevers_ts (&vers);
  716.         return (1);
  717.     case T_UPTODATE:
  718.         break;
  719.     default:
  720.         error (0, 0, "CVS internal error: unknown status %d", status);
  721.         break;
  722.     }
  723.  
  724.     freevers_ts (&vers);
  725.     return (0);
  726. }
  727.  
  728. /*
  729.  * Print warm fuzzies while examining the dirs
  730.  */
  731. /* ARGSUSED */
  732. static Dtype
  733. check_direntproc (dir, repos, update_dir)
  734.     char *dir;
  735.     char *repos;
  736.     char *update_dir;
  737. {
  738.     if (!quiet)
  739.     error (0, 0, "Examining %s", update_dir);
  740.  
  741.     return (R_PROCESS);
  742. }
  743.  
  744. /*
  745.  * Walklist proc to run pre-commit checks
  746.  */
  747. static int
  748. precommit_list_proc (p, closure)
  749.     Node *p;
  750.     void *closure;
  751. {
  752.     if (p->data == (char *) T_ADDED || p->data == (char *) T_MODIFIED ||
  753.     p->data == (char *) T_REMOVED)
  754.     {
  755.     run_arg (p->key);
  756.     }
  757.     return (0);
  758. }
  759.  
  760. /*
  761.  * Callback proc for pre-commit checking
  762.  */
  763. static List *ulist;
  764. static int
  765. precommit_proc (repository, filter)
  766.     char *repository;
  767.     char *filter;
  768. {
  769.     /* see if the filter is there, only if it's a full path */
  770.     if (isabsolute (filter))
  771.     {
  772.         char *s, *cp;
  773.     
  774.     s = xstrdup (filter);
  775.     for (cp = s; *cp; cp++)
  776.         if (isspace (*cp))
  777.         {
  778.         *cp = '\0';
  779.         break;
  780.         }
  781.     if (!isfile (s))
  782.     {
  783.         error (0, errno, "cannot find pre-commit filter `%s'", s);
  784.         free (s);
  785.         return (1);            /* so it fails! */
  786.     }
  787.     free (s);
  788.     }
  789.  
  790.     run_setup ("%s %s", filter, repository);
  791.     (void) walklist (ulist, precommit_list_proc, NULL);
  792.     return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
  793. }
  794.  
  795. /*
  796.  * Run the pre-commit checks for the dir
  797.  */
  798. /* ARGSUSED */
  799. static int
  800. check_filesdoneproc (err, repos, update_dir)
  801.     int err;
  802.     char *repos;
  803.     char *update_dir;
  804. {
  805.     int n;
  806.     Node *p;
  807.  
  808.     /* find the update list for this dir */
  809.     p = findnode (mulist, update_dir);
  810.     if (p != NULL)
  811.     ulist = ((struct master_lists *) p->data)->ulist;
  812.     else
  813.     ulist = (List *) NULL;
  814.  
  815.     /* skip the checks if there's nothing to do */
  816.     if (ulist == NULL || ulist->list->next == ulist->list)
  817.     return (err);
  818.  
  819.     /* run any pre-commit checks */
  820.     if ((n = Parse_Info (CVSROOTADM_COMMITINFO, repos, precommit_proc, 1)) > 0)
  821.     {
  822.     error (0, 0, "Pre-commit check failed");
  823.     err += n;
  824.     }
  825.  
  826.     return (err);
  827. }
  828.  
  829. /*
  830.  * Do the work of committing a file
  831.  */
  832. static int maxrev;
  833. static char sbranch[PATH_MAX];
  834.  
  835. /* ARGSUSED */
  836. static int
  837. commit_fileproc (finfo)
  838.     struct file_info *finfo;
  839. {
  840.     Node *p;
  841.     int err = 0;
  842.     List *ulist, *cilist;
  843.     struct commit_info *ci;
  844.     char rcs[PATH_MAX];
  845.  
  846.     if (finfo->update_dir[0] == '\0')
  847.     p = findnode (mulist, ".");
  848.     else
  849.     p = findnode (mulist, finfo->update_dir);
  850.  
  851.     /*
  852.      * if p is null, there were file type command line args which were
  853.      * all up-to-date so nothing really needs to be done
  854.      */
  855.     if (p == NULL)
  856.     return (0);
  857.     ulist = ((struct master_lists *) p->data)->ulist;
  858.     cilist = ((struct master_lists *) p->data)->cilist;
  859.  
  860.     /*
  861.      * At this point, we should have the commit message unless we were called
  862.      * with files as args from the command line.  In that latter case, we
  863.      * need to get the commit message ourselves
  864.      */
  865.     if (use_editor && !got_message)
  866.       {
  867.     got_message = 1;
  868.     do_editor (finfo->update_dir, &message, finfo->repository, ulist);
  869.       }
  870.  
  871.     p = findnode (cilist, finfo->file);
  872.     if (p == NULL)
  873.     return (0);
  874.  
  875.     ci = (struct commit_info *) p->data;
  876.     if (ci->status == T_MODIFIED)
  877.     {
  878.     if (lockrcsfile (finfo->file, finfo->repository, ci->rev) != 0)
  879.     {
  880.         unlockrcs (finfo->file, finfo->repository);
  881.         err = 1;
  882.         goto out;
  883.     }
  884.     }
  885.     else if (ci->status == T_ADDED)
  886.     {
  887.     if (checkaddfile (finfo->file, finfo->repository, ci->tag, ci->options,
  888.               &finfo->rcs) != 0)
  889.     {
  890.         fixaddfile (finfo->file, finfo->repository);
  891.         err = 1;
  892.         goto out;
  893.     }
  894.  
  895.     /* adding files with a tag, now means adding them on a branch.
  896.        Since the branch test was done in check_fileproc for
  897.        modified files, we need to stub it in again here. */
  898.  
  899.     if (ci->tag) {
  900.         locate_rcs (finfo->file, finfo->repository, rcs);
  901.         ci->rev = RCS_whatbranch (finfo->rcs, ci->tag);
  902.         err = Checkin ('A', finfo->file, finfo->update_dir, finfo->repository, rcs, ci->rev,
  903.                ci->tag, ci->options, message, finfo->entries);
  904.         if (err != 0)
  905.         {
  906.         unlockrcs (finfo->file, finfo->repository);
  907.         fixbranch (finfo->file, finfo->repository, sbranch);
  908.         }
  909.  
  910.         (void) time (&last_register_time);
  911.  
  912.         ci->status = T_UPTODATE;
  913.     }
  914.     }
  915.  
  916.     /*
  917.      * Add the file for real
  918.      */
  919.     if (ci->status == T_ADDED)
  920.     {
  921.     char *xrev = (char *) NULL;
  922.  
  923.     if (ci->rev == NULL)
  924.     {
  925.         /* find the max major rev number in this directory */
  926.         maxrev = 0;
  927.         (void) walklist (finfo->entries, findmaxrev, NULL);
  928.         if (maxrev == 0)
  929.         maxrev = 1;
  930.         xrev = xmalloc (20);
  931.         (void) sprintf (xrev, "%d", maxrev);
  932.     }
  933.  
  934.     /* XXX - an added file with symbolic -r should add tag as well */
  935.     err = finaladd (finfo->file, ci->rev ? ci->rev : xrev, ci->tag, ci->options,
  936.             finfo->update_dir, finfo->repository, finfo->entries);
  937.     if (xrev)
  938.         free (xrev);
  939.     }
  940.     else if (ci->status == T_MODIFIED)
  941.     {
  942.     locate_rcs (finfo->file, finfo->repository, rcs);
  943.     err = Checkin ('M', finfo->file, finfo->update_dir, finfo->repository,
  944.                rcs, ci->rev, ci->tag,
  945.                ci->options, message, finfo->entries);
  946.  
  947.     (void) time (&last_register_time);
  948.  
  949.     if (err != 0)
  950.     {
  951.         unlockrcs (finfo->file, finfo->repository);
  952.         fixbranch (finfo->file, finfo->repository, sbranch);
  953.     }
  954.     }
  955.     else if (ci->status == T_REMOVED)
  956.     {
  957.     err = remove_file (finfo->file, finfo->repository, ci->tag, message,
  958.                finfo->entries, finfo->rcs);
  959. #ifdef SERVER_SUPPORT
  960.     if (server_active) {
  961.         server_scratch_entry_only ();
  962.         server_updated (finfo->file, finfo->update_dir, finfo->repository,
  963.                 /* Doesn't matter, it won't get checked.  */
  964.                 SERVER_UPDATED, (struct stat *) NULL,
  965.                 (unsigned char *) NULL);
  966.     }
  967. #endif
  968.     }
  969.  
  970.     /* Clearly this is right for T_MODIFIED.  I haven't thought so much
  971.        about T_ADDED or T_REMOVED.  */
  972.     notify_do ('C', finfo->file, getcaller (), NULL, NULL, finfo->repository);
  973.  
  974. out:
  975.     if (err != 0)
  976.     {
  977.     /* on failure, remove the file from ulist */
  978.     p = findnode (ulist, finfo->file);
  979.     if (p)
  980.         delnode (p);
  981.     }
  982.  
  983.     return (err);
  984. }
  985.  
  986. /*
  987.  * Log the commit and clean up the update list
  988.  */
  989. /* ARGSUSED */
  990. static int
  991. commit_filesdoneproc (err, repository, update_dir)
  992.     int err;
  993.     char *repository;
  994.     char *update_dir;
  995. {
  996.     char *xtag = (char *) NULL;
  997.     Node *p;
  998.     List *ulist;
  999.  
  1000.     p = findnode (mulist, update_dir);
  1001.     if (p == NULL)
  1002.     return (err);
  1003.  
  1004.     ulist = ((struct master_lists *) p->data)->ulist;
  1005.  
  1006.     got_message = 0;
  1007.  
  1008.     /* see if we need to specify a per-directory or -r option tag */
  1009.     if (tag == NULL)
  1010.     ParseTag (&xtag, (char **) NULL);
  1011.  
  1012.     Update_Logfile (repository, message, tag ? tag : xtag, (FILE *) 0, ulist);
  1013.     if (xtag)
  1014.     free (xtag);
  1015.  
  1016.     /* Build the administrative files if necessary.  */
  1017.     {
  1018.     char *p;
  1019.  
  1020.     if (strncmp (CVSroot, repository, strlen (CVSroot)) != 0)
  1021.         error (0, 0, "internal error: repository doesn't begin with root");
  1022.     p = repository + strlen (CVSroot);
  1023.     if (*p == '/')
  1024.         ++p;
  1025.     if (strcmp ("CVSROOT", p) == 0)
  1026.     {
  1027.         /* "Database" might a little bit grandiose and/or vague,
  1028.            but "checked-out copies of administrative files, unless
  1029.            in the case of modules and you are using ndbm in which
  1030.            case modules.{pag,dir,db}" is verbose and excessively
  1031.            focused on how the database is implemented.  */
  1032.  
  1033.         cvs_output (program_name, 0);
  1034.         cvs_output (" ", 1);
  1035.         cvs_output (command_name, 0);
  1036.         cvs_output (": Rebuilding administrative file database\n", 0);
  1037.         mkmodules (repository);
  1038.     }
  1039.     }
  1040.  
  1041.     if (err == 0 && run_module_prog)
  1042.     {
  1043.     FILE *fp;
  1044.  
  1045.     if ((fp = fopen (CVSADM_CIPROG, "r")) != NULL)
  1046.     {
  1047.         char *line;
  1048.         int line_length;
  1049.         size_t line_chars_allocated;
  1050.         char *repository;
  1051.  
  1052.         line = NULL;
  1053.         line_chars_allocated = 0;
  1054.         line_length = getline (&line, &line_chars_allocated, fp);
  1055.         if (line_length > 0)
  1056.         {
  1057.         /* Remove any trailing newline.  */
  1058.         if (line[line_length - 1] == '\n')
  1059.             line[--line_length] = '\0';
  1060.         repository = Name_Repository ((char *) NULL, update_dir);
  1061.         run_setup ("%s %s", line, repository);
  1062.         (void) printf ("%s %s: Executing '", program_name,
  1063.                    command_name);
  1064.         run_print (stdout);
  1065.         (void) printf ("'\n");
  1066.         (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  1067.         free (repository);
  1068.         }
  1069.         else
  1070.         {
  1071.         if (ferror (fp))
  1072.             error (0, errno, "warning: error reading %s",
  1073.                CVSADM_CIPROG);
  1074.         }
  1075.         if (line != NULL)
  1076.         free (line);
  1077.         if (fclose (fp) < 0)
  1078.         error (0, errno, "warning: cannot close %s", CVSADM_CIPROG);
  1079.     }
  1080.     else
  1081.     {
  1082.         if (! existence_error (errno))
  1083.         error (0, errno, "warning: cannot open %s", CVSADM_CIPROG);
  1084.     }
  1085.     }
  1086.  
  1087.     return (err);
  1088. }
  1089.  
  1090. /*
  1091.  * Get the log message for a dir and print a warm fuzzy
  1092.  */
  1093. /* ARGSUSED */
  1094. static Dtype
  1095. commit_direntproc (dir, repos, update_dir)
  1096.     char *dir;
  1097.     char *repos;
  1098.     char *update_dir;
  1099. {
  1100.     Node *p;
  1101.     List *ulist;
  1102.     char *real_repos;
  1103.  
  1104.     /* find the update list for this dir */
  1105.     p = findnode (mulist, update_dir);
  1106.     if (p != NULL)
  1107.     ulist = ((struct master_lists *) p->data)->ulist;
  1108.     else
  1109.     ulist = (List *) NULL;
  1110.  
  1111.     /* skip the files as an optimization */
  1112.     if (ulist == NULL || ulist->list->next == ulist->list)
  1113.     return (R_SKIP_FILES);
  1114.  
  1115.     /* print the warm fuzzy */
  1116.     if (!quiet)
  1117.     error (0, 0, "Committing %s", update_dir);
  1118.  
  1119.     /* get commit message */
  1120.     if (use_editor)
  1121.     {
  1122.     got_message = 1;
  1123.     real_repos = Name_Repository (dir, update_dir);
  1124.     do_editor (update_dir, &message, real_repos, ulist);
  1125.     free (real_repos);
  1126.     }
  1127.     return (R_PROCESS);
  1128. }
  1129.  
  1130. /*
  1131.  * Process the post-commit proc if necessary
  1132.  */
  1133. /* ARGSUSED */
  1134. static int
  1135. commit_dirleaveproc (dir, err, update_dir)
  1136.     char *dir;
  1137.     int err;
  1138.     char *update_dir;
  1139. {
  1140.     /* update the per-directory tag info */
  1141.     if (err == 0 && write_dirtag != NULL)
  1142.     {
  1143.     WriteTag ((char *) NULL, write_dirtag, (char *) NULL);
  1144. #ifdef SERVER_SUPPORT
  1145.     if (server_active)
  1146.         server_set_sticky (update_dir, Name_Repository (dir, update_dir),
  1147.                    write_dirtag, (char *) NULL);
  1148. #endif
  1149.     }
  1150.  
  1151.     return (err);
  1152. }
  1153.  
  1154. /*
  1155.  * find the maximum major rev number in an entries file
  1156.  */
  1157. static int
  1158. findmaxrev (p, closure)
  1159.     Node *p;
  1160.     void *closure;
  1161. {
  1162.     char *cp;
  1163.     int thisrev;
  1164.     Entnode *entdata;
  1165.  
  1166.     entdata = (Entnode *) p->data;
  1167.     cp = strchr (entdata->version, '.');
  1168.     if (cp != NULL)
  1169.     *cp = '\0';
  1170.     thisrev = atoi (entdata->version);
  1171.     if (cp != NULL)
  1172.     *cp = '.';
  1173.     if (thisrev > maxrev)
  1174.     maxrev = thisrev;
  1175.     return (0);
  1176. }
  1177.  
  1178. /*
  1179.  * Actually remove a file by moving it to the attic
  1180.  * XXX - if removing a ,v file that is a relative symbolic link to
  1181.  * another ,v file, we probably should add a ".." component to the
  1182.  * link to keep it relative after we move it into the attic.
  1183.  */
  1184. static int
  1185. remove_file (file, repository, tag, message, entries, rcsnode)
  1186.     char *file;
  1187.     char *repository;
  1188.     char *tag;
  1189.     char *message;
  1190.     List *entries;
  1191.     RCSNode *rcsnode;
  1192. {
  1193.     mode_t omask;
  1194.     int retcode;
  1195.     char rcs[PATH_MAX];
  1196.     char *tmp;
  1197.  
  1198.     int branch;
  1199.     int lockflag;
  1200.     char *corev;
  1201.     char *rev;
  1202.     char *prev_rev;
  1203.  
  1204.     corev = NULL;
  1205.     rev = NULL;
  1206.     prev_rev = NULL;
  1207.  
  1208.     retcode = 0;
  1209.  
  1210.     locate_rcs (file, repository, rcs);
  1211.  
  1212.     branch = 0;
  1213.     if (tag && !(branch = RCS_isbranch (rcsnode, tag)))
  1214.     {
  1215.     /* a symbolic tag is specified; just remove the tag from the file */
  1216.     if ((retcode = RCS_deltag (rcs, tag, 1)) != 0) 
  1217.     {
  1218.         if (!quiet)
  1219.         error (0, retcode == -1 ? errno : 0,
  1220.                "failed to remove tag `%s' from `%s'", tag, rcs);
  1221.         return (1);
  1222.     }
  1223.     Scratch_Entry (entries, file);
  1224.     return (0);
  1225.     }
  1226.  
  1227.     /* we are removing the file from either the head or a branch */
  1228.     /* commit a new, dead revision. */
  1229.  
  1230.     /* Print message indicating that file is going to be removed. */
  1231.     (void) printf ("Removing %s;\n", file);
  1232.  
  1233.     rev = NULL;
  1234.     lockflag = RCS_FLAGS_LOCK;
  1235.     if (branch)
  1236.     {
  1237.     char *branchname;
  1238.  
  1239.     rev = RCS_whatbranch (rcsnode, tag);
  1240.     if (rev == NULL)
  1241.     {
  1242.         error (0, 0, "cannot find branch \"%s\".", tag);
  1243.         return (1);
  1244.     }
  1245.     
  1246.     if (rcsnode == NULL)
  1247.     {
  1248.         error (0, 0, "boy, I'm confused.");
  1249.         return (1);
  1250.     }
  1251.     branchname = RCS_getbranch (rcsnode, rev, 1);
  1252.     if (branchname == NULL)
  1253.     {
  1254.         /* no revision exists on this branch.  use the previous
  1255.            revision but do not lock. */
  1256.         corev = RCS_gettag (rcsnode, tag, 1, 0);
  1257.         prev_rev = xstrdup(rev);
  1258.         lockflag = 0;
  1259.     } else
  1260.     {
  1261.         corev = xstrdup (rev);
  1262.         prev_rev = xstrdup(branchname);
  1263.         free (branchname);
  1264.     }
  1265.  
  1266.     } else  /* Not a branch */
  1267.     {
  1268.  
  1269.         /* Get current head revision of file. */
  1270.     if (rcsnode == NULL)
  1271.     {
  1272.         error (0, 0, "could not find parsed rcsfile %s", file);
  1273.         return (1);
  1274.     }
  1275.     prev_rev = RCS_head (rcsnode);
  1276.     }
  1277.     
  1278.     /* if removing without a tag or a branch, then make sure the default
  1279.        branch is the trunk. */
  1280.     if (!tag && !branch)
  1281.     {
  1282.         if (RCS_setbranch (rcs, NULL) != 0) 
  1283.     {
  1284.         error (0, 0, "cannot change branch to default for %s",
  1285.            rcs);
  1286.         return (1);
  1287.     }
  1288.     }
  1289.  
  1290. #ifdef SERVER_SUPPORT
  1291.     if (server_active) {
  1292.     /* If this is the server, there will be a file sitting in the
  1293.        temp directory which is the kludgy way in which server.c
  1294.        tells time_stamp that the file is no longer around.  Remove
  1295.        it so we can create temp files with that name (ignore errors).  */
  1296.     unlink_file (file);
  1297.     }
  1298. #endif
  1299.  
  1300.     /* check something out.  Generally this is the head.  If we have a
  1301.        particular rev, then name it.  except when creating a branch,
  1302.        lock the rev we're checking out.  */
  1303.     retcode = RCS_checkout (rcs, "", rev ? corev : NULL, NULL, RUN_TTY,
  1304.                             lockflag, 1);
  1305.     if (retcode != 0)
  1306.     {
  1307.     if (!quiet)
  1308.         error (0, retcode == -1 ? errno : 0,
  1309.            "failed to check out `%s'", rcs);
  1310.     return (1);
  1311.     }
  1312.  
  1313.     if (corev != NULL)
  1314.     free (corev);
  1315.  
  1316.     retcode = RCS_checkin (rcs, NULL, message, rev, RCS_FLAGS_DEAD, 1);
  1317.     if (retcode    != 0)
  1318.     {
  1319.     if (!quiet)
  1320.         error (0, retcode == -1 ? errno : 0,
  1321.            "failed to commit dead revision for `%s'", rcs);
  1322.     return (1);
  1323.     }
  1324.  
  1325.     if (rev != NULL)
  1326.     free (rev);
  1327.  
  1328.     if (!branch)
  1329.     {
  1330.     /* this was the head; really move it into the Attic */
  1331.     tmp = xmalloc(strlen(repository) + 
  1332.               sizeof('/') +
  1333.               sizeof(CVSATTIC) +
  1334.               sizeof('/') +
  1335.               strlen(file) +
  1336.               sizeof(RCSEXT) + 1);
  1337.     (void) sprintf (tmp, "%s/%s", repository, CVSATTIC);
  1338.     omask = umask (cvsumask);
  1339.     (void) CVS_MKDIR (tmp, 0777);
  1340.     (void) umask (omask);
  1341.     (void) sprintf (tmp, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
  1342.     
  1343.     if (strcmp (rcs, tmp) != 0
  1344.         && rename (rcs, tmp) == -1
  1345.         && (isreadable (rcs) || !isreadable (tmp)))
  1346.     {
  1347.         free(tmp);
  1348.         return (1);
  1349.     }
  1350.     free(tmp);
  1351.     }
  1352.  
  1353.     /* Print message that file was removed. */
  1354.     (void) printf ("%s  <--  %s\n", rcs, file);
  1355.     (void) printf ("new revision: delete; ");
  1356.     (void) printf ("previous revision: %s\n", prev_rev);
  1357.     (void) printf ("done\n");
  1358.     free(prev_rev);
  1359.  
  1360.     Scratch_Entry (entries, file);
  1361.     return (0);
  1362. }
  1363.  
  1364. /*
  1365.  * Do the actual checkin for added files
  1366.  */
  1367. static int
  1368. finaladd (file, rev, tag, options, update_dir, repository, entries)
  1369.     char *file;
  1370.     char *rev;
  1371.     char *tag;
  1372.     char *options;
  1373.     char *update_dir;
  1374.     char *repository;
  1375.     List *entries;
  1376. {
  1377.     int ret;
  1378.     char tmp[PATH_MAX];
  1379.     char rcs[PATH_MAX];
  1380.  
  1381.     locate_rcs (file, repository, rcs);
  1382.     ret = Checkin ('A', file, update_dir, repository, rcs, rev, tag, options,
  1383.            message, entries);
  1384.     if (ret == 0)
  1385.     {
  1386.     (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
  1387.     (void) unlink_file (tmp);
  1388.     }
  1389.     else
  1390.     fixaddfile (file, repository);
  1391.  
  1392.     (void) time (&last_register_time);
  1393.  
  1394.     return (ret);
  1395. }
  1396.  
  1397. /*
  1398.  * Unlock an rcs file
  1399.  */
  1400. static void
  1401. unlockrcs (file, repository)
  1402.     char *file;
  1403.     char *repository;
  1404. {
  1405.     char rcs[PATH_MAX];
  1406.     int retcode = 0;
  1407.  
  1408.     locate_rcs (file, repository, rcs);
  1409.  
  1410.     if ((retcode = RCS_unlock (rcs, NULL, 0)) != 0)
  1411.     error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1412.            "could not unlock %s", rcs);
  1413. }
  1414.  
  1415. /*
  1416.  * remove a partially added file.  if we can parse it, leave it alone.
  1417.  */
  1418. static void
  1419. fixaddfile (file, repository)
  1420.     char *file;
  1421.     char *repository;
  1422. {
  1423.     RCSNode *rcsfile;
  1424.     char rcs[PATH_MAX];
  1425.     int save_really_quiet;
  1426.  
  1427.     locate_rcs (file, repository, rcs);
  1428.     save_really_quiet = really_quiet;
  1429.     really_quiet = 1;
  1430.     if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
  1431.     (void) unlink_file (rcs);
  1432.     else
  1433.     freercsnode (&rcsfile);
  1434.     really_quiet = save_really_quiet;
  1435. }
  1436.  
  1437. /*
  1438.  * put the branch back on an rcs file
  1439.  */
  1440. static void
  1441. fixbranch (file, repository, branch)
  1442.     char *file;
  1443.     char *repository;
  1444.     char *branch;
  1445. {
  1446.     char rcs[PATH_MAX];
  1447.     int retcode = 0;
  1448.  
  1449.     if (branch != NULL && branch[0] != '\0')
  1450.     {
  1451.     locate_rcs (file, repository, rcs);
  1452.     if ((retcode = RCS_setbranch (rcs, branch)) != 0)
  1453.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1454.            "cannot restore branch to %s for %s", branch, rcs);
  1455.     }
  1456. }
  1457.  
  1458. /*
  1459.  * do the initial part of a file add for the named file.  if adding
  1460.  * with a tag, put the file in the Attic and point the symbolic tag
  1461.  * at the committed revision.
  1462.  */
  1463.  
  1464. static int
  1465. checkaddfile (file, repository, tag, options, rcsnode)
  1466.     char *file;
  1467.     char *repository;
  1468.     char *tag;
  1469.     char *options;
  1470.     RCSNode **rcsnode;
  1471. {
  1472.     char rcs[PATH_MAX];
  1473.     char fname[PATH_MAX];
  1474.     mode_t omask;
  1475.     int retcode = 0;
  1476.     int newfile = 0;
  1477.  
  1478.     if (tag)
  1479.     {
  1480.     (void) sprintf(rcs, "%s/%s", repository, CVSATTIC);
  1481.     omask = umask (cvsumask);
  1482.     if (CVS_MKDIR (rcs, 0777) != 0 && errno != EEXIST)
  1483.         error (1, errno, "cannot make directory `%s'", rcs);;
  1484.     (void) umask (omask);
  1485.     (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
  1486.     }
  1487.     else
  1488.     locate_rcs (file, repository, rcs);
  1489.  
  1490.     if (isreadable(rcs))
  1491.     {
  1492.     /* file has existed in the past.  Prepare to resurrect. */
  1493.     char oldfile[PATH_MAX];
  1494.     char *rev;
  1495.     RCSNode *rcsfile;
  1496.  
  1497.     if (tag == NULL)
  1498.     {
  1499.         /* we are adding on the trunk, so move the file out of the
  1500.            Attic. */
  1501.         strcpy (oldfile, rcs);
  1502.         sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
  1503.         
  1504.         if (strcmp (oldfile, rcs) == 0
  1505.         || rename (oldfile, rcs) != 0
  1506.         || isreadable (oldfile)
  1507.         || !isreadable (rcs))
  1508.         {
  1509.         error (0, 0, "failed to move `%s' out of the attic.",
  1510.                file);
  1511.         return (1);
  1512.         }
  1513.     }
  1514.  
  1515.     if ((rcsfile = *rcsnode) == NULL)
  1516.     {
  1517.         error (0, 0, "could not find parsed rcsfile %s", file);
  1518.         return (1);
  1519.     }
  1520.  
  1521.     rev = RCS_getversion (rcsfile, tag, NULL, 1, 0);
  1522.     /* and lock it */
  1523.     if (lock_RCS (file, rcs, rev, repository)) {
  1524.         error (0, 0, "cannot lock `%s'.", rcs);
  1525.         free (rev);
  1526.         return (1);
  1527.     }
  1528.  
  1529.     free (rev);
  1530.     } else {
  1531.     /* this is the first time we have ever seen this file; create
  1532.        an rcs file.  */
  1533.     run_setup ("%s%s -x,v/ -i", Rcsbin, RCS);
  1534.  
  1535.     (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
  1536.     /* If the file does not exist, no big deal.  In particular, the
  1537.        server does not (yet at least) create CVSEXT_LOG files.  */
  1538.     if (isfile (fname))
  1539.         run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG);
  1540.  
  1541.     /* Set RCS keyword expansion options.  */
  1542.     if (options && options[0] == '-' && options[1] == 'k')
  1543.         run_arg (options);
  1544.     run_arg (rcs);
  1545.     if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
  1546.     {
  1547.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1548.            "could not create %s", rcs);
  1549.         return (1);
  1550.     }
  1551.     newfile = 1;
  1552.     }
  1553.  
  1554.     /* when adding a file for the first time, and using a tag, we need
  1555.        to create a dead revision on the trunk.  */
  1556.     if (tag && newfile)
  1557.     {
  1558.     char *tmp;
  1559.  
  1560.     /* move the new file out of the way. */
  1561.     (void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
  1562.     rename_file (file, fname);
  1563.     copy_file (DEVNULL, file);
  1564.  
  1565.     tmp = xmalloc (strlen (file) + strlen (tag) + 80);
  1566.     /* commit a dead revision. */
  1567.     (void) sprintf (tmp, "file %s was initially added on branch %s.",
  1568.             file, tag);
  1569.     retcode = RCS_checkin (rcs, NULL, tmp, NULL,
  1570.                    RCS_FLAGS_DEAD | RCS_FLAGS_QUIET, 0);
  1571.     free (tmp);
  1572.     if (retcode != 0)
  1573.     {
  1574.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1575.            "could not create initial dead revision %s", rcs);
  1576.         return (1);
  1577.     }
  1578.  
  1579.     /* put the new file back where it was */
  1580.     rename_file (fname, file);
  1581.     
  1582.     /* and lock it once again. */
  1583.     if (lock_RCS (file, rcs, NULL, repository)) {
  1584.         error (0, 0, "cannot lock `%s'.", rcs);
  1585.         return (1);
  1586.     }
  1587.     }
  1588.  
  1589.     if (tag != NULL)
  1590.     {
  1591.     /* when adding with a tag, we need to stub a branch, if it
  1592.        doesn't already exist.  */
  1593.     RCSNode *rcsfile;
  1594.  
  1595.     rcsfile = RCS_parse (file, repository);
  1596.     if (rcsfile == NULL)
  1597.     {
  1598.         error (0, 0, "could not read %s", rcs);
  1599.         return (1);
  1600.     }
  1601.     
  1602.     if (!RCS_nodeisbranch (rcsfile, tag)) {
  1603.         /* branch does not exist.  Stub it.  */
  1604.         char *head;
  1605.         char *magicrev;
  1606.         
  1607.         head = RCS_getversion (rcsfile, NULL, NULL, 0, 0);
  1608.         magicrev = RCS_magicrev (rcsfile, head);
  1609.         if ((retcode = RCS_settag(rcs, tag, magicrev)) != 0)
  1610.         {
  1611.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  1612.                "could not stub branch %s for %s", tag, rcs);
  1613.         return (1);
  1614.         }
  1615.         
  1616.         freercsnode (&rcsfile);
  1617.         
  1618.         /* reparse the file, then add it to our list. */
  1619.         rcsfile = RCS_parse (file, repository);
  1620.         if (rcsfile == NULL)
  1621.         {
  1622.         error (0, 0, "could not reparse %s", rcs);
  1623.         return (1);
  1624.         }
  1625.  
  1626.         free (head);
  1627.         free (magicrev);
  1628.     }
  1629.     else
  1630.     {
  1631.         /* lock the branch. (stubbed branches need not be locked.)  */
  1632.         if (lock_RCS (file, rcs, NULL, repository)) {
  1633.         error (0, 0, "cannot lock `%s'.", rcs);
  1634.         return (1);
  1635.         }
  1636.     } 
  1637.  
  1638.     if (rcsnode)
  1639.         freercsnode(rcsnode);
  1640.     *rcsnode = rcsfile;
  1641.     }
  1642.  
  1643.     fileattr_newfile (file);
  1644.  
  1645.     fix_rcs_modes (rcs, file);
  1646.     return (0);
  1647. }
  1648.  
  1649. /*
  1650.  * Lock the rcs file ``file''
  1651.  */
  1652. static int
  1653. lockrcsfile (file, repository, rev)
  1654.     char *file;
  1655.     char *repository;
  1656.     char *rev;
  1657. {
  1658.     char rcs[PATH_MAX];
  1659.  
  1660.     locate_rcs (file, repository, rcs);
  1661.     if (lock_RCS (file, rcs, rev, repository) != 0)
  1662.     return (1);
  1663.     else
  1664.     return (0);
  1665. }
  1666.  
  1667. /*
  1668.  * Attempt to place a lock on the RCS file; returns 0 if it could and 1 if it
  1669.  * couldn't.  If the RCS file currently has a branch as the head, we must
  1670.  * move the head back to the trunk before locking the file, and be sure to
  1671.  * put the branch back as the head if there are any errors.
  1672.  */
  1673. static int
  1674. lock_RCS (user, rcs, rev, repository)
  1675.     char *user;
  1676.     char *rcs;
  1677.     char *rev;
  1678.     char *repository;
  1679. {
  1680.     RCSNode *rcsfile;
  1681.     char *branch = NULL;
  1682.     int err = 0;
  1683.  
  1684.     /*
  1685.      * For a specified, numeric revision of the form "1" or "1.1", (or when
  1686.      * no revision is specified ""), definitely move the branch to the trunk
  1687.      * before locking the RCS file.
  1688.      * 
  1689.      * The assumption is that if there is more than one revision on the trunk,
  1690.      * the head points to the trunk, not a branch... and as such, it's not
  1691.      * necessary to move the head in this case.
  1692.      */
  1693.     if (rev == NULL || (rev && isdigit (*rev) && numdots (rev) < 2))
  1694.     {
  1695.     if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
  1696.     {
  1697.         /* invalid rcs file? */
  1698.         err = 1;
  1699.     }
  1700.     else
  1701.     {
  1702.         /* rcsfile is valid */
  1703.         branch = xstrdup (rcsfile->branch);
  1704.         freercsnode (&rcsfile);
  1705.         if (branch != NULL)
  1706.         {
  1707.         if (RCS_setbranch (rcs, NULL) != 0)
  1708.         {
  1709.             error (0, 0, "cannot change branch to default for %s",
  1710.                rcs);
  1711.             if (branch)
  1712.             free (branch);
  1713.             return (1);
  1714.         }
  1715.         }
  1716.         err = RCS_lock(rcs, NULL, 0);
  1717.     }
  1718.     }
  1719.     else
  1720.     {
  1721.     (void) RCS_lock(rcs, rev, 1);
  1722.     }
  1723.  
  1724.     if (err == 0)
  1725.     {
  1726.     if (branch)
  1727.     {
  1728.         (void) strcpy (sbranch, branch);
  1729.         free (branch);
  1730.     }
  1731.     else
  1732.         sbranch[0] = '\0';
  1733.     return (0);
  1734.     }
  1735.  
  1736.     /* try to restore the branch if we can on error */
  1737.     if (branch != NULL)
  1738.     fixbranch (user, repository, branch);
  1739.  
  1740.     if (branch)
  1741.     free (branch);
  1742.     return (1);
  1743. }
  1744.  
  1745. /*
  1746.  * Called when "add"ing files to the RCS respository, as it is necessary to
  1747.  * preserve the file modes in the same fashion that RCS does.  This would be
  1748.  * automatic except that we are placing the RCS ,v file very far away from
  1749.  * the user file, and I can't seem to convince RCS of the location of the
  1750.  * user file.  So we munge it here, after the ,v file has been successfully
  1751.  * initialized with "rcs -i".
  1752.  */
  1753. static void
  1754. fix_rcs_modes (rcs, user)
  1755.     char *rcs;
  1756.     char *user;
  1757. {
  1758.     struct stat sb;
  1759.  
  1760.     if (stat (user, &sb) != -1)
  1761.     (void) chmod (rcs, (int) sb.st_mode & ~0222);
  1762. }
  1763.  
  1764. /*
  1765.  * free an UPDATE node's data (really nothing to do)
  1766.  */
  1767. void
  1768. update_delproc (p)
  1769.     Node *p;
  1770. {
  1771.     p->data = (char *) NULL;
  1772. }
  1773.  
  1774. /*
  1775.  * Free the commit_info structure in p.
  1776.  */
  1777. static void
  1778. ci_delproc (p)
  1779.     Node *p;
  1780. {
  1781.     struct commit_info *ci;
  1782.  
  1783.     ci = (struct commit_info *) p->data;
  1784.     if (ci->rev)
  1785.     free (ci->rev);
  1786.     if (ci->tag)
  1787.     free (ci->tag);
  1788.     if (ci->options)
  1789.     free (ci->options);
  1790.     free (ci);
  1791. }
  1792.  
  1793. /*
  1794.  * Free the commit_info structure in p.
  1795.  */
  1796. static void
  1797. masterlist_delproc (p)
  1798.     Node *p;
  1799. {
  1800.     struct master_lists *ml;
  1801.  
  1802.     ml = (struct master_lists *) p->data;
  1803.     dellist (&ml->ulist);
  1804.     dellist (&ml->cilist);
  1805.     free (ml);
  1806. }
  1807.  
  1808. /*
  1809.  * Find an RCS file in the repository.
  1810.  */
  1811. static void
  1812. locate_rcs (file, repository, rcs)
  1813.     char *file;
  1814.     char *repository;
  1815.     char *rcs;
  1816. {
  1817.     (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
  1818.     if (!isreadable (rcs))
  1819.     {
  1820.     (void) sprintf (rcs, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
  1821.     if (!isreadable (rcs))
  1822.         (void) sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
  1823.     }
  1824. }
  1825.